/**
 * tools.expose 1.0.5 - Make HTML elements stand out
 * 
 * Copyright (c) 2009 Tero Piirainen
 * http://flowplayer.org/tools/expose.html
 *
 * Dual licensed under MIT and GPL 2+ licenses
 * http://www.opensource.org/licenses
 *
 * Launch  : June 2008
 * Date: ${date}
 * Revision: ${revision} 
 */
(function ($)
{

	// static constructs
	$.tools = $.tools || {};

	$.tools.expose = {
		version: '1.0.5',
		conf: {

			// mask settings
			maskId: null,
			loadSpeed: 'slow',
			closeSpeed: 'fast',
			closeOnClick: true,
			closeOnEsc: true,

			// css settings
			zIndex: 9998,
			opacity: 0.8,
			color: '#456',
			api: false
		}
	};

	/* one of the greatest headaches in the tool. finally made it */
	function viewport()
	{

		// the horror case
		if ($.browser.msie)
		{

			// if there are no scrollbars then use window.height
			var d = $(document).height(), w = $(window).height();

			return [
				window.innerWidth || 							// ie7+
				document.documentElement.clientWidth || 	// ie6  
				document.body.clientWidth, 					// ie6 quirks mode
				d - w < 20 ? w : d
			];
		}

		// other well behaving browsers
		return [$(window).width(), $(document).height()];

	}

	function Expose(els, conf)
	{

		// private variables
		var self = this, $self = $(this), mask = null, loaded = false, origIndex = 0;

		// bind all callbacks from configuration
		$.each(conf, function (name, fn)
		{
			if ($.isFunction(fn)) { $self.bind(name, fn); }
		});

		// adjust mask size when window is resized (or firebug is toggled)
		$(window).resize(function ()
		{
			self.fit();
		});


		// public methods
		$.extend(this, {

			getMask: function ()
			{
				return mask;
			},

			getExposed: function ()
			{
				return els;
			},

			getConf: function ()
			{
				return conf;
			},

			isLoaded: function ()
			{
				return loaded;
			},

			load: function (e)
			{

				// already loaded ?
				if (loaded) { return self; }

				origIndex = els.eq(0).css("zIndex");

				// find existing mask
				if (conf.maskId) { mask = $("#" + conf.maskId); }

				if (!mask || !mask.length)
				{

					var size = viewport();

					mask = $('<div/>').css({
						position: 'absolute',
						top: 0,
						left: 0,
						width: size[0],
						height: size[1],
						display: 'none',
						opacity: 0,
						zIndex: conf.zIndex
					});

					// id
					if (conf.maskId) { mask.attr("id", conf.maskId); }

					$("body").append(mask);


					// background color 
					var bg = mask.css("backgroundColor");

					if (!bg || bg == 'transparent' || bg == 'rgba(0, 0, 0, 0)')
					{
						mask.css("backgroundColor", conf.color);
					}

					// esc button
					if (conf.closeOnEsc)
					{
						$(document).bind("keydown.unexpose", function (evt)
						{
							if (evt.keyCode == 27)
							{
								self.close();
							}
						});
					}

					// mask click closes
					if (conf.closeOnClick)
					{
						mask.bind("click.unexpose", function (e)
						{
							self.close(e);
						});
					}
				}

				// possibility to cancel click action
				e = e || $.Event();
				e.type = "onBeforeLoad";
				$self.trigger(e);

				if (e.isDefaultPrevented()) { return self; }

				// make sure element is positioned absolutely or relatively
				$.each(els, function ()
				{
					var el = $(this);
					if (!/relative|absolute|fixed/i.test(el.css("position")))
					{
						el.css("position", "relative");
					}
				});

				// make elements sit on top of the mask				
				els.css({ zIndex: Math.max(conf.zIndex + 1, origIndex == 'auto' ? 0 : origIndex) });

				// fit mask
				self.fit();

				// reveal mask
				var h = mask.height();

				if (!this.isLoaded())
				{
					mask.css({ opacity: 0, display: 'block' }).fadeTo(conf.loadSpeed, conf.opacity, function ()
					{
						// sometimes IE6 misses the height property on fadeTo method
						if (mask.height() != h) { mask.css("height", h); }
						e.type = "onLoad";
						$self.trigger(e);

					});
				}

				loaded = true;

				return self;
			},


			close: function (e)
			{

				if (!loaded) { return self; }

				e = e || $.Event();
				e.type = "onBeforeClose";
				$self.trigger(e);
				if (e.isDefaultPrevented()) { return self; }

				mask.fadeOut(conf.closeSpeed, function ()
				{
					e.type = "onClose";
					$self.trigger(e);
					els.css({ zIndex: $.browser.msie ? origIndex : null });
				});

				loaded = false;
				return self;
			},

			fit: function ()
			{
				if (mask)
				{
					var size = viewport();
					mask.css({ width: size[0], height: size[1] });
				}
			},

			bind: function (name, fn)
			{
				$self.bind(name, fn);
				return self;
			},

			unbind: function (name)
			{
				$self.unbind(name);
				return self;
			}

		});

		// callbacks	
		$.each("onBeforeLoad,onLoad,onBeforeClose,onClose".split(","), function (i, ev)
		{
			self[ev] = function (fn)
			{
				return self.bind(ev, fn);
			};
		});

	}


	// jQuery plugin implementation
	$.fn.expose = function (conf)
	{

		var el = this.eq(typeof conf == 'number' ? conf : 0).data("expose");
		if (el) { return el; }

		if (typeof conf == 'string')
		{
			conf = { color: conf };
		}

		var globals = $.extend({}, $.tools.expose.conf);
		conf = $.extend(globals, conf);

		// construct exposes
		this.each(function ()
		{
			el = new Expose($(this), conf);
			$(this).data("expose", el);
		});

		return conf.api ? el : this;
	};


})(jQuery);
